﻿using System;
using System.Collections.Generic;
using System.Threading;

namespace Close.io.Client
{
	/// <summary>
	/// This is based on code that I cannot find the source, props to whoever wrote the original code.
	/// </summary>
	public static class Retry
	{

		private  static TimeSpan defaultPause = TimeSpan.FromSeconds(1);
		private const int retryCount = 3;

		public static void Do(Action action, TimeSpan retryInterval, int retryCount = 3)
		{
			Do<object>(() =>
			{
				action();
				return null;
			}, retryInterval, retryCount);
		}

		public static void Do(Action action,  int retryCount = 3)
		{
			Do<object>(() =>
			{
				action();
				return null;
			}, defaultPause, retryCount);
		}

		public static R Do<R>(Func<R> action, TimeSpan retryInterval, int retryCount = 3)
		{
			var exceptions = new List<Exception>();

			for (int retry = 0; retry < retryCount; retry++)
			{
				try
				{
					return action();
				}
				catch (Exception ex)
				{
					exceptions.Add(ex);
					Thread.Sleep(retryInterval);
				}
			}

			throw new AggregateException(exceptions);
		}

		public static R Do<T1, R>(Func<T1, R> action, T1 param1,  TimeSpan retryInterval, int retryCount = 3)
		{
			var exceptions = new List<Exception>();

			for (int retry = 0; retry < retryCount; retry++)
			{
				try
				{
					return action(param1);
				}
				catch (Exception ex)
				{
					exceptions.Add(ex);
					Thread.Sleep(retryInterval);
				}
			}

			throw new AggregateException(exceptions);
		}

		public static R Do<T1, T2, R>(Func<T1, T2, R> action, T1 param1, T2 param2, TimeSpan retryInterval, int retryCount = 3)
		{
			var exceptions = new List<Exception>();

			for (int retry = 0; retry < retryCount; retry++)
			{
				try
				{
					return action(param1, param2);
				}
				catch (Exception ex)
				{
					exceptions.Add(ex);
					Thread.Sleep(retryInterval);
				}
			}

			throw new AggregateException(exceptions);
		}
	
		public static R Do<T1, T2, T3, R>(Func<T1, T2, T3, R> action, T1 param1, T2 param2, T3 param3, TimeSpan retryInterval, int retryCount = 3)
		{
			var exceptions = new List<Exception>();

			for (int retry = 0; retry < retryCount; retry++)
			{
				try
				{
					return action(param1, param2, param3);
				}
				catch (Exception ex)
				{
					exceptions.Add(ex);
					Thread.Sleep(retryInterval);
				}
			}

			throw new AggregateException(exceptions);
		}

		public static R Do<T1, R>(Func<T1, R> action, T1 param1)
		{
			var exceptions = new List<Exception>();

			for (int retry = 0; retry < retryCount; retry++)
			{
				try
				{
					return action(param1);
				}
				catch (Exception ex)
				{
					exceptions.Add(ex);
					Thread.Sleep(retryCount);
				}
			}

			throw new AggregateException(exceptions);
		}

		public static R Do<T1, T2, R>(Func<T1, T2, R> action, T1 param1, T2 param2 )
		{
			var exceptions = new List<Exception>();

			for (int retry = 0; retry < retryCount; retry++)
			{
				try
				{
					return action(param1, param2);
				}
				catch (Exception ex)
				{
					exceptions.Add(ex);
					Thread.Sleep(retryCount);
				}
			}

			throw new AggregateException(exceptions);
		}

		public static R Do<T1, T2, T3, R>(Func<T1, T2, T3, R> action, T1 param1, T2 param2, T3 param3 )
		{
			var exceptions = new List<Exception>();

			for (int retry = 0; retry < retryCount; retry++)
			{
				try
				{
					return action(param1, param2, param3);
				}
				catch (Exception ex)
				{
					exceptions.Add(ex);
					Thread.Sleep(retryCount);
				}
			}

			throw new AggregateException(exceptions);
		}

	
	
	}
}